home *** CD-ROM | disk | FTP | other *** search
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License
- * at http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- * the License for the specific language governing rights and
- * limitations under the License.
- *
- * The Original Code is the Places Tagging Service.
- *
- * The Initial Developer of the Original Code is
- * Mozilla Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2007
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Asaf Romano <mano@mozilla.com> (Original Author)
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
- const Cc = Components.classes;
- const Ci = Components.interfaces;
- const Cr = Components.results;
-
- Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-
- const NH_CONTRACTID = "@mozilla.org/browser/nav-history-service;1";
- const BMS_CONTRACTID = "@mozilla.org/browser/nav-bookmarks-service;1";
- const IO_CONTRACTID = "@mozilla.org/network/io-service;1";
- const ANNO_CONTRACTID = "@mozilla.org/browser/annotation-service;1";
- const FAV_CONTRACTID = "@mozilla.org/browser/favicon-service;1";
- const OBSS_CONTRACTID = "@mozilla.org/observer-service;1";
-
- var gIoService = Cc[IO_CONTRACTID].getService(Ci.nsIIOService);
-
- /**
- * The Places Tagging Service
- */
- function TaggingService() {
- }
-
- TaggingService.prototype = {
- get _bms() {
- if (!this.__bms)
- this.__bms = Cc[BMS_CONTRACTID].getService(Ci.nsINavBookmarksService);
- return this.__bms;
- },
-
- get _history() {
- if (!this.__history)
- this.__history = Cc[NH_CONTRACTID].getService(Ci.nsINavHistoryService);
- return this.__history;
- },
-
- get _annos() {
- if (!this.__annos)
- this.__annos = Cc[ANNO_CONTRACTID].getService(Ci.nsIAnnotationService);
- return this.__annos;
- },
-
- get _tagsResult() {
- if (!this.__tagsResult) {
- var options = this._history.getNewQueryOptions();
- var query = this._history.getNewQuery();
- query.setFolders([this._bms.tagsFolder], 1);
- this.__tagsResult = this._history.executeQuery(query, options);
- this.__tagsResult.root.containerOpen = true;
-
- // we need to null out the result on shutdown
- var observerSvc = Cc[OBSS_CONTRACTID].getService(Ci.nsIObserverService);
- observerSvc.addObserver(this, "xpcom-shutdown", false);
- }
- return this.__tagsResult;
- },
-
- // Feed XPCOMUtils
- classDescription: "Places Tagging Service",
- contractID: "@mozilla.org/browser/tagging-service;1",
- classID: Components.ID("{bbc23860-2553-479d-8b78-94d9038334f7}"),
-
- // nsISupports
- QueryInterface: XPCOMUtils.generateQI([Ci.nsITaggingService,
- Ci.nsIObserver]),
-
- /**
- * If there's no tag with the given name, null is returned;
- */
- _getTagNode: function TS__getTagIndex(aTagNameOrId) {
- if (!aTagNameOrId)
- throw Cr.NS_ERROR_INVALID_ARG;
-
- var nameLower = null;
- if (typeof(aTagNameOrId) == "string")
- nameLower = aTagNameOrId.toLowerCase();
-
- var root = this._tagsResult.root;
- var cc = root.childCount;
- for (var i=0; i < cc; i++) {
- var child = root.getChild(i);
- if ((nameLower && child.title.toLowerCase() == nameLower) ||
- child.itemId === aTagNameOrId)
- return child;
- }
-
- return null;
- },
-
- /**
- * Creates a tag container under the tags-root with the given name.
- *
- * @param aName
- * the name for the new container.
- * @returns the id of the new container.
- */
- _createTag: function TS__createTag(aName) {
- return this._bms.createFolder(this._bms.tagsFolder, aName,
- this._bms.DEFAULT_INDEX);
- },
-
- /**
- * Checks whether the given uri is tagged with the given tag.
- *
- * @param [in] aURI
- * url to check for
- * @param [in] aTagId
- * id of the folder representing the tag to check
- * @param [out] aItemId
- * the id of the item found under the tag container
- * @returns true if the given uri is tagged with the given tag, false
- * otherwise.
- */
- _isURITaggedInternal: function TS__uriTagged(aURI, aTagId, aItemId) {
- var bookmarkIds = this._bms.getBookmarkIdsForURI(aURI, {});
- for (var i=0; i < bookmarkIds.length; i++) {
- var parent = this._bms.getFolderIdForItem(bookmarkIds[i]);
- if (parent == aTagId) {
- aItemId.value = bookmarkIds[i];
- return true;
- }
- }
- return false;
- },
-
- // nsITaggingService
- tagURI: function TS_tagURI(aURI, aTags) {
- if (!aURI || !aTags)
- throw Cr.NS_ERROR_INVALID_ARG;
-
- for (var i=0; i < aTags.length; i++) {
- var tagNode = this._getTagNode(aTags[i]);
- if (!tagNode) {
- if (typeof(aTags[i]) == "number")
- throw Cr.NS_ERROR_INVALID_ARG;
-
- var tagId = this._createTag(aTags[i]);
- this._bms.insertBookmark(tagId, aURI, this._bms.DEFAULT_INDEX, null);
- }
- else {
- var tagId = tagNode.itemId;
- if (!this._isURITaggedInternal(aURI, tagNode.itemId, {}))
- this._bms.insertBookmark(tagId, aURI, this._bms.DEFAULT_INDEX, null);
-
- // _getTagNode ignores case sensitivity
- // rename the tag container so the places view would match the
- // user-typed values
- if (typeof(aTags[i]) == "string" && tagNode.title != aTags[i])
- this._bms.setItemTitle(tagNode.itemId, aTags[i]);
- }
- }
- },
-
- /**
- * Removes the tag container from the tags-root if the given tag is empty.
- *
- * @param aTagId
- * the item-id of the tag element under the tags root
- */
- _removeTagIfEmpty: function TS__removeTagIfEmpty(aTagId) {
- var node = this._getTagNode(aTagId).QueryInterface(Ci.nsINavHistoryContainerResultNode);
- var wasOpen = node.containerOpen;
- if (!wasOpen)
- node.containerOpen = true;
- var cc = node.childCount;
- if (wasOpen)
- node.containerOpen = false;
- if (cc == 0) {
- this._bms.removeFolder(node.itemId);
- }
- },
-
- // nsITaggingService
- untagURI: function TS_untagURI(aURI, aTags) {
- if (!aURI)
- throw Cr.NS_ERROR_INVALID_ARG;
-
- if (!aTags) {
- // see IDL.
- // XXXmano: write a perf-sensitive version of this code path...
- aTags = this.getTagsForURI(aURI, { });
- }
-
- for (var i=0; i < aTags.length; i++) {
- var tagNode = this._getTagNode(aTags[i]);
- if (tagNode) {
- var itemId = { };
- if (this._isURITaggedInternal(aURI, tagNode.itemId, itemId)) {
- this._bms.removeItem(itemId.value);
- this._removeTagIfEmpty(tagNode.itemId);
- }
- }
- else if (typeof(aTags[i]) == "number")
- throw Cr.NS_ERROR_INVALID_ARG;
- }
- },
-
- // nsITaggingService
- getURIsForTag: function TS_getURIsForTag(aTag) {
- if (aTag.length == 0)
- throw Cr.NS_ERROR_INVALID_ARG;
-
- var uris = [];
- var tagNode = this._getTagNode(aTag);
- if (tagNode) {
- tagNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);
- tagNode.containerOpen = true;
- var cc = tagNode.childCount;
- for (var i = 0; i < cc; i++) {
- try {
- uris.push(gIoService.newURI(tagNode.getChild(i).uri, null, null));
- } catch (ex) {
- // This is an invalid node, tags should only contain valid uri nodes.
- // continue to next node.
- }
- }
- tagNode.containerOpen = false;
- }
- return uris;
- },
-
- // nsITaggingService
- getTagsForURI: function TS_getTagsForURI(aURI, aCount) {
- if (!aURI)
- throw Cr.NS_ERROR_INVALID_ARG;
-
- var tags = [];
- var bookmarkIds = this._bms.getBookmarkIdsForURI(aURI, {});
- var root = this._tagsResult.root;
- var cc = root.childCount;
- for (var i=0; i < bookmarkIds.length; i++) {
- var parent = this._bms.getFolderIdForItem(bookmarkIds[i]);
- for (var j=0; j < cc; j++) {
- var child = root.getChild(j);
- if (child.itemId == parent)
- tags.push(child.title);
- }
- }
-
- // sort the tag list
- tags.sort();
- aCount.value = tags.length;
- return tags;
- },
-
- // nsITaggingService
- get allTags() {
- var tags = [];
- var root = this._tagsResult.root;
- var cc = root.childCount;
- for (var j=0; j < cc; j++) {
- var child = root.getChild(j);
- tags.push(child.title);
- }
-
- // sort the tag list
- tags.sort();
- return tags;
- },
-
- // nsIObserver
- observe: function TS_observe(subject, topic, data) {
- if (topic == "xpcom-shutdown") {
- this.__tagsResult.root.containerOpen = false;
- this.__tagsResult = null;
- var observerSvc = Cc[OBSS_CONTRACTID].getService(Ci.nsIObserverService);
- observerSvc.removeObserver(this, "xpcom-shutdown");
- }
- }
- };
-
- function NSGetModule(compMgr, fileSpec) {
- return XPCOMUtils.generateModule([TaggingService]);
- }
-